package org.example.server.goods.repo;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import org.example.api.goods.category.request.CategoryCreateRequest;
import org.example.api.goods.category.request.CategorySearchWithLevelRequest;
import org.example.api.goods.category.request.CategoryUpdateRequest;
import org.example.frw.api.base.intf.ID;
import org.example.frw.api.base.intf.Pageable;
import org.example.frw.api.base.intf.impl.IDPojo;
import org.example.server.goods.entity.CategoryEntity;
import org.example.server.goods.mapper.CategoryMapper;
import org.example.server.goods.mapstruct.category.CategoryConverter;
import org.example.server.goods.repo.doo.CategorySummaryDo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class CategoryRepo implements CategoryRepoInterface{
  private final CategoryMapper categoryMapper;
  private final CategoryConverter categoryConverter;

  public CategoryRepo(CategoryMapper categoryMapper, CategoryConverter categoryConverter) {
    this.categoryMapper = categoryMapper;
    this.categoryConverter = categoryConverter;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public ID insert(CategoryCreateRequest category) {
    CategoryEntity parentEntity = null;
    if(ObjectUtil.isNotNull(category.getCategoryParentId())){
      parentEntity = categoryMapper.selectById(category.getCategoryParentId());
      Assert.notNull(parentEntity,"父分类[{}]不存在",category.getCategoryParentId());
    }

    LambdaQueryWrapper<CategoryEntity> countWrapper = new LambdaQueryWrapper<CategoryEntity>()
            .eq(CategoryEntity::getCategoryName, category.getCategoryName());
    Long count = categoryMapper.selectCount(countWrapper);
    Assert.isTrue(count == 0,"分类[{}]已存在，请勿重复创建",category.getCategoryName());

    CategoryEntity categoryEntity = new CategoryEntity();
    categoryEntity.setCategoryName(category.getCategoryName());
    categoryEntity.setCategoryParentId(ObjectUtil.defaultIfNull(category.getCategoryParentId(), 0L));
    categoryEntity.setIsParent(false);
    categoryEntity.setSort(category.getSort());
    categoryEntity.setCategoryImageUrl(category.getCategoryImageUrl());
    categoryEntity.setIsDisplay(category.getIsDisplay());
    categoryEntity.setLevel(0);
    categoryEntity.setPath("/"+category.getCategoryName());
    categoryEntity.setPathId("");
    categoryEntity.setUnit(category.getUnit());

    if(parentEntity != null){
      // 从父亲继承level和path
      categoryEntity.setLevel(parentEntity.getLevel() + 1);
      categoryEntity.setPath(parentEntity.getPath()+"/"+category.getCategoryName());

      parentEntity.setIsParent(true);
      categoryMapper.updateById(parentEntity);
    }

    categoryMapper.insert(categoryEntity);

    String pathId = "/" +categoryEntity.getId();
    if(parentEntity != null){
      pathId = parentEntity.getPathId() + pathId;
    }
    categoryEntity.setPathId(pathId);

    categoryMapper.updateById(categoryEntity);
    return new IDPojo(categoryEntity.getId());
  }

  @Override
  public List<CategorySummaryDo> pageCategories(Pageable page, CategorySearchWithLevelRequest search) {
    Page<CategoryEntity> entityPage = categoryMapper.selectPage(new Page<>(page.current(), page.pageSize()), searchCategoriesQueryWrapper(search));
    if(CollUtil.isEmpty(entityPage.getRecords())){
      return new ArrayList<>();
    }
    // 批量查询父分类名称
    List<CategoryEntity> parentCategories = categoryMapper.selectBatchIds(Lists.newArrayList(entityPage.getRecords().stream().map(CategoryEntity::getCategoryParentId).collect(Collectors.toList())));
    Map<Long, CategoryEntity> parentEntieyMap = parentCategories.stream().collect(Collectors.toMap(CategoryEntity::getId, Function.identity(), (o1, o2) -> o1));
    entityPage.getRecords().forEach(r->{
      CategoryEntity categoryEntity = parentEntieyMap.get(r.getCategoryParentId());
      if(categoryEntity != null){
        r.setCategoryParentName(categoryEntity.getCategoryName());
      }
    });
    return categoryConverter.toCategorySummaryFromEntity(entityPage.getRecords());
  }

  @Override
  public Long categoriesCount(CategorySearchWithLevelRequest search) {
    return categoryMapper.selectCount(searchCategoriesQueryWrapper(search));
  }

  @Override
  public CategorySummaryDo findOne(ID id) {
    CategoryEntity categoryEntity = categoryMapper.selectById(id.id());
    if(categoryEntity == null){
      return null;
    }
    return categoryConverter.toCategorySummaryFromEntity(categoryEntity);
  }

  @Override
  public void hide(ID id) {
    assertIdExist(id);

    LambdaUpdateWrapper<CategoryEntity> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.set(CategoryEntity::getIsDisplay,false);
    updateWrapper.eq(CategoryEntity::getId,id.id());
    categoryMapper.update(updateWrapper);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void updateCategory(ID id, CategoryUpdateRequest updateRequest) {
    CategoryEntity entity = assertIdExist(id);
    String categoryName = entity.getCategoryName();
    if (!CharSequenceUtil.equals(categoryName, updateRequest.getCategoryName())) {
      // 不相等就更新name，同时递归更新后代path
      categoryMapper.updateDescendantsPath(id.id(), categoryName, updateRequest.getCategoryName());
    }
    // 不相等才更新
    boolean nameNoEqual = ObjectUtil.notEqual(entity.getCategoryName(), updateRequest.getCategoryName());
    boolean sortNoEqual = ObjectUtil.notEqual(entity.getSort(), updateRequest.getSort());
    boolean imageNotEqual = ObjectUtil.notEqual(entity.getCategoryImageUrl(), updateRequest.getCategoryImageUrl());
    boolean unitNotEqual = ObjectUtil.notEqual(entity.getUnit(), updateRequest.getUnit());
    boolean isDisplayNotEqual = ObjectUtil.notEqual(entity.getIsDisplay(), updateRequest.getIsDisplay());
    if (sortNoEqual || imageNotEqual || unitNotEqual || isDisplayNotEqual) {
      LambdaUpdateWrapper<CategoryEntity> updateWrapper = new LambdaUpdateWrapper<CategoryEntity>()
              .set(nameNoEqual,CategoryEntity::getCategoryName,updateRequest.getCategoryName())
              .set(sortNoEqual, CategoryEntity::getSort, updateRequest.getSort())
              .set(imageNotEqual, CategoryEntity::getCategoryImageUrl, updateRequest.getCategoryImageUrl())
              .set(unitNotEqual, CategoryEntity::getUnit, updateRequest.getUnit())
              .set(isDisplayNotEqual, CategoryEntity::getIsDisplay, updateRequest.getIsDisplay())
              .eq(CategoryEntity::getId, id.id());
      categoryMapper.update(null, updateWrapper);
    }
  }

  private CategoryEntity assertIdExist(ID id) {
    CategoryEntity entity = categoryMapper.selectById(id.id());
    Assert.notNull(entity,"分类找不到 {}", id.id());
    return entity;
  }

  private static LambdaQueryWrapper<CategoryEntity> searchCategoriesQueryWrapper(CategorySearchWithLevelRequest search) {
    return new LambdaQueryWrapper<CategoryEntity>()
            .like(CharSequenceUtil.isNotBlank(search.getCategoryName()), CategoryEntity::getCategoryName, search.getCategoryName())
            .eq(ObjectUtil.isNotNull(search.getCategoryParentId()), CategoryEntity::getCategoryParentId, search.getCategoryParentId())
            .eq(search.getIsDisplay() != null, CategoryEntity::getIsDisplay, search.getIsDisplay())
            .and(wrapper -> {
              if(search.getMinLevel() == null && search.getLevel() != null){
                wrapper.eq(CategoryEntity::getLevel,search.getLevel());
                return;
              }

              wrapper.ge(CategoryEntity::getLevel, search.getMinLevel() == null ? 0 : search.getMinLevel());
            });
  }

}